package models

import (
	"time"

	"github.com/EDDYCJY/go-gin-example/pkg/e"
	"github.com/jinzhu/gorm"
)

type SubmitMachineRecord struct {
	// 提交记录id
	ID uint64 `gorm:"column:id" json:"id"`
	// 机器所有者id
	OwnerUserId uint64 `gorm:"column:owner_user_id" json:"owner_user_id"`
	// 机器id
	NodeId string `gorm:"column:node_id" json:"node_id"`
	// 国家
	Country string `gorm:"column:country" json:"country"`
	// 城市
	City string `gorm:"column:city" json:"city"`
	// cpu型号
	CpuName string `gorm:"column:cpu_name" json:"cpu_name"`
	// cpu核心数
	CpuCores uint32 `gorm:"column:cpu_cores" json:"cpu_cores"`
	// cpu频率（MHz）
	CpuFrequency uint32 `gorm:"column:cpu_frequency" json:"cpu_frequency"`
	// 内存大小（MB）
	MemSize uint64 `gorm:"column:mem_size" json:"mem_size"`
	// 磁盘大小（GB）
	DiskSize uint64 `gorm:"column:disk_size" json:"disk_size"`
	// 显卡型号
	GpuName string `gorm:"column:gpu_name" json:"gpu_name"`
	// 显存大小（MB）
	GpuMemSize uint64 `gorm:"column:gpu_mem_size" json:"gpu_mem_size"`
	// GPU数量
	GpuCount uint32 `gorm:"column:gpu_count" json:"gpu_count"`
	// 上行带宽（Mbps）
	UpBandwidth uint32 `gorm:"column:up_bandwidth" json:"up_bandwidth"`
	// 下行带宽（Mbps）
	DownBandwidth uint32 `gorm:"column:down_bandwidth" json:"down_bandwidth"`
	// 提交时间
	SubmitTime DbTime `gorm:"column:submit_time" json:"submit_time"`
	// 上线时间
	OnlineTime DbTime `gorm:"column:online_time" json:"online_time"`
	// 下线时间
	OfflineTime DbTime `gorm:"column:offline_time" json:"offline_time"`
	// 更新时间
	UpdateTime DbTime `gorm:"column:update_time" json:"update_time"`
}

func (SubmitMachineRecord) TableName() string {
	return "submit_machines"
}

func GetSubmitMachineByNodeId(node_id string) (*SubmitMachineRecord, e.FResult) {
	s := &SubmitMachineRecord{}
	err := db.Debug().Where("node_id = ?", node_id).First(s).Error
	if err != nil {
		return nil, e.FResult_ERROR
	}
	return s, e.FResult_OK
}

// 创建新记录
func CreateSubmitMachineRecord(data *SubmitMachineRecord) (uint64, e.FResult) {
	db := db.Begin()
	// 查重
	var online_count uint64 = 0	
	err := db.Model(&SubmitMachineRecord{}).Debug().
		Select("id").
		Where("node_id = ?", data.NodeId).
		Where("offline_time > ?", time.Now().UTC()).
		Count(&online_count).Error
	if online_count > 0 {
		return 0, e.FResult{ErrCode: e.ERROR, ErrMsg: "提交的机器还未下线，不能重复提交"}
	}

	// 写入机器提交记录
	err = db.Debug().Select("owner_user_id", "node_id", "country", "city", "cpu_name", "cpu_cores",
		"cpu_frequency", "mem_size", "disk_size", "gpu_name", "gpu_mem_size", "gpu_count", "up_bandwidth",
		"down_bandwidth", "offline_time").
		Create(data).Error
	if err != nil {
		db.Rollback()
		return 0, e.FResult_ERROR
	}
	db.Commit()
	return data.ID, e.FResult_OK
}

// 根据记录id，获取一条机器提交记录
func GetSubmitMachineRecordById(id int64) (*SubmitMachineRecord, e.FResult) {
	var data SubmitMachineRecord
	err := db.Model(&data).Debug().Where("id = ?", id).First(&data).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, e.FResult{ErrCode: e.ERROR, ErrMsg: "机器不存在"}
		} else {
			return nil, e.FResult_ERROR
		}
	}
	return &data, e.FResult_OK
}

// 根据机器id，获取此机器的提交记录列表
func GetSubmitMachineRecordsByMachineId(node_id int64) ([]SubmitMachineRecord, e.FResult) {
	var datas []SubmitMachineRecord
	err := db.Model(&datas).Debug().Where("node_id = ?", node_id).First(&datas).Error
	if err != nil && err != gorm.ErrRecordNotFound {
		return nil, e.FResult_ERROR
	}
	return datas, e.FResult_OK
}

// 根据记录id，更新记录字段
func UpdateSubmitMachineRecordById(id int64, datas map[string]interface{}) e.FResult {
	err := db.Model(&SubmitMachineRecord{}).Debug().Where("id =? ", id).Updates(datas).Error
	if err != nil {
		return e.FResult_ERROR
	} else {
		return e.FResult_OK
	}
}

// 获取所有国家列表
type CountryListItem struct {
	Country string
}

func GetAllContries() ([]string, e.FResult) {
	var datas []string

	var country_list []CountryListItem
	err := db.Model(&SubmitMachineRecord{}).Debug().
		Select("distinct(country)").Scan(&country_list).Error
	if err != nil && err != gorm.ErrRecordNotFound {
		return nil, e.FResult_ERROR
	}

	for _, v := range country_list {
		datas = append(datas, v.Country)
	}

	return datas, e.FResult_OK
}

// 获取所有城市列表
type CityListItem struct {
	City string
}

func GetAllCities() ([]string, e.FResult) {
	var datas []string

	var city_list []CityListItem
	err := db.Model(&SubmitMachineRecord{}).Debug().
		Select("distinct(city)").Scan(&city_list).Error
	if err != nil && err != gorm.ErrRecordNotFound {
		return nil, e.FResult_ERROR
	}

	for _, v := range city_list {
		datas = append(datas, v.City)
	}

	return datas, e.FResult_OK
}

// 获取所有gpu型号列表
type GpuNameListItem struct {
	GpuName string
}

func GetAllGpuNames() ([]string, e.FResult) {
	var datas []string

	var gpu_name_list []GpuNameListItem
	err := db.Model(&SubmitMachineRecord{}).Debug().
		Select("distinct(gpu_name)").Scan(&gpu_name_list).Error
	if err != nil && err != gorm.ErrRecordNotFound {
		return nil, e.FResult_ERROR
	}

	for _, v := range gpu_name_list {
		datas = append(datas, v.GpuName)
	}

	return datas, e.FResult_OK
}

// 获取最大的gpu数量
type MaxGpuCountItem struct {
	GpuCount int32
}

func GetMaxGpuCount() (int32, e.FResult) {
	var max_gpu_count_list []MaxGpuCountItem
	err := db.Model(&SubmitMachineRecord{}).Debug().
		Select("max(gpu_count) as gpu_count").Scan(&max_gpu_count_list).Error
	if err != nil && err != gorm.ErrRecordNotFound {
		return 0, e.FResult_ERROR
	}

	if len(max_gpu_count_list) > 0 {
		return max_gpu_count_list[0].GpuCount, e.FResult_OK
	} else {
		return 0, e.FResult_OK
	}
}

// 根据筛选条件，查询机器列表
type SubmitMachineRecordInfo struct {
	SubmitMachineRecord
	RentStartTime DbTime
	RentEndTime   DbTime
}

func GetOnlineSubmitMachineRecords(page uint32, limit uint32,
	fields_where map[string]interface{}) ([]SubmitMachineRecordInfo, e.FResult) {
	var offset uint32
	if page <= 0 {
		offset = 0
	} else {
		offset = limit * (page - 1)
	}
	db := db.Model(&SubmitMachineRecord{}).Debug()
	if fields_where != nil {
		db = db.Where(fields_where)
	}

	var datas []SubmitMachineRecordInfo
	var t_now = time.Now().UTC()
	db = db.
		Select("submit_machines.*, max(rent_orders.rent_start_time) as rent_start_time, max(rent_orders.rent_end_time) as rent_end_time").
		Joins("left join rent_orders on rent_orders.submit_machine_record_id = submit_machines.id").
		Group("submit_machines.id").
		Where("online_time <= ? and offline_time > ?", t_now, t_now)
	db = db.Order("rent_end_time asc")
	err := db.Offset(offset).Limit(limit).Scan(&datas).Error
	if err != nil && err != gorm.ErrRecordNotFound {
		return nil, e.FResult_ERROR
	} else {
		return datas, e.FResult_OK
	}
}

// 上述列表的总数量
func GetOnlineSubmitMachineRecordsCount(fields_where map[string]interface{}) (int64, e.FResult) {
	db := db.Model(&SubmitMachineRecord{}).Debug()
	if fields_where != nil {
		db = db.Where(fields_where)
	}
	var count int64 = 0
	var t_now = time.Now().UTC()
	err := db.Where("online_time <= ? and offline_time > ?", t_now, t_now).
		Count(&count).Error
	if err != nil {
		return 0, e.FResult_ERROR
	} else {
		return count, e.FResult_OK
	}
}

type TasksUsedResource struct {
	TaskID string
	// 已使用的cpu核心数
	CpuCores uint32
	// 已使用的gpu数量
	GpuCount uint32
	// 已使用的内存大小（MB）
	MemSize uint64
	// 已使用的磁盘大小（GB）
	DiskSize uint64
}

func GetMachineUsedResource(record_id uint64) *TasksUsedResource {
	var data TasksUsedResource

	var t_now = time.Now().UTC()
	var rent_orders []RentOrder
	db.Model(&RentOrder{}).Debug().
		Where("submit_machine_record_id = ?", record_id).
		Where("rent_start_time <= ? and rent_end_time > ?", t_now, t_now).
		Find(&rent_orders)
	if len(rent_orders) > 0 {
		if rent_orders[0].TaskId == "all" {
			data.TaskID = rent_orders[0].TaskId
		} else {
			for _, v := range rent_orders {
				var item VmModify
				err := db.Model(&VmModify{}).Debug().
					Where("rent_order_id = ?", v.ID).
					Order("id desc").First(&item).Error
				if err == nil {
					data.TaskID = v.TaskId
					data.GpuCount += item.GpuCount
					data.MemSize += item.MemSize
					data.DiskSize += item.DiskSize
					data.CpuCores += item.CpuCores
				}
			}
		}

		return &data
	} else {
		return nil
	}
}

// 获取用户已提交的所有机器列表
type SubmitMachineInfo struct {
	SubmitMachineRecord
	MaxRentStartTime DbTime
	MaxRentEndTime   DbTime
	TotalIncomeCoins float64
}

func GetSubmitMachinesByUserId(userId uint64, page uint32, limit uint32) ([]SubmitMachineInfo, e.FResult) {
	var offset uint32
	if page <= 0 {
		offset = 0
	} else {
		offset = limit * (page - 1)
	}

	var datas []SubmitMachineInfo

	var submit_machines []SubmitMachineRecord
	db.Model(&SubmitMachineRecord{}).Debug().
		Where("owner_user_id = ?", userId).
		Order("submit_time desc").
		Offset(offset).Limit(limit).Scan(&submit_machines)

	for _, machine := range submit_machines {
		var data SubmitMachineInfo
		data.SubmitMachineRecord = machine

		var total_coins float64
		var rent_orders []RentOrder
		db.Model(&RentOrder{}).Debug().
			Where("submit_machine_record_id = ?", machine.ID).
			Scan(&rent_orders)
		for _, order := range rent_orders {
			type order_payment struct {
				TotalPaymentCoins float64
			}

			var sum_coins1 order_payment
			db.Model(&PaymentRecord{}).Debug().
				Select("sum(payment_coins) as total_payment_coins").
				Where("payment_type = '1' or payment_type = '2' or payment_type = '4'").
				Where("record_id = ?", order.ID).
				Scan(&sum_coins1)
			total_coins += sum_coins1.TotalPaymentCoins

			var vms []VmModify
			db.Model(&VmModify{}).Debug().
				Where("rent_order_id = ?", order.ID).
				Scan(&vms)
			for _, vm := range vms {
				var sum_coins2 order_payment
				db.Model(&PaymentRecord{}).Debug().
					Select("sum(payment_coins) as total_payment_coins").
					Where("payment_type = '3' or payment_type = '5'").
					Where("record_id = ?", vm.ID).
					Scan(&sum_coins2)
				total_coins += sum_coins2.TotalPaymentCoins
			}
		}
		data.TotalIncomeCoins = total_coins

		type max_rent_time struct {
			MaxRentStartTime DbTime
			MaxRentEndTime   DbTime
		}
		var rent_times max_rent_time
		db.Model(&RentOrder{}).Debug().
			Select("max(rent_start_time) as max_rent_start_time, max(rent_end_time) as max_rent_end_time").
			Where("submit_machine_record_id = ?", machine.ID).
			Scan(&rent_times)
		data.MaxRentStartTime = rent_times.MaxRentStartTime
		data.MaxRentEndTime = rent_times.MaxRentEndTime

		datas = append(datas, data)
	}

	return datas, e.FResult_OK
}

// 上述列表的总数量
func GetSubmitMachinesCountByUserId(userId uint64) (int32, e.FResult) {
	var submit_machines_count int32
	err := db.Model(&SubmitMachineRecord{}).Debug().
		Where("owner_user_id = ?", userId).
		Count(&submit_machines_count).Error
	if err != nil {
		return submit_machines_count, e.FResult_ERROR
	} else {
		return submit_machines_count, e.FResult_OK
	}
}

// 根据记录id，获取一条机器提交记录
func GetSubmitMachineRecordByIdAndOwnerId(id uint64, ownerId uint64) (*SubmitMachineRecord, e.FResult) {
	var data SubmitMachineRecord
	err := db.Model(&data).Debug().Where("id = ? and owner_user_id = ?", id, ownerId).First(&data).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, e.FResult{ErrCode: e.ERROR, ErrMsg: "机器不存在"}
		} else {
			return nil, e.FResult_ERROR
		}
	}
	return &data, e.FResult_OK
}

// 机器下线
func OfflineSubmitMachine(id uint64) e.FResult {
	var t_now = time.Now().UTC()
	err := db.Model(&SubmitMachineRecord{}).Debug().Where("id = ? ", id).
		Updates(map[string]interface{}{
			"offline_time": t_now,
		}).Error
	if err != nil {
		return e.FResult_ERROR
	}
	return e.FResult_OK
}

type WalletRent struct {
	RentType uint32 `json:"rent_type"`
	TaskID   string `json:"task_id"`
	Wallet   string `json:"wallet"`
	RentEnd  int64  `json:"rent_end"`
}
type MachineStatus struct {
	Status     string       `json:"status"`
	RentWallet []WalletRent `json:"wallet_renters"` //[rent_wallet, rent_end(秒,utc时间)]
}

func GetMachineStatus(node_id string) *MachineStatus {
	var retStatus MachineStatus
	var rent_wallets []WalletRent

	var record SubmitMachineRecord
	t_now := time.Now().UTC()
	err := db.Model(&SubmitMachineRecord{}).Debug().
		Where("node_id=?", node_id).
		Where("online_time <= ? and offline_time > ?", t_now, t_now).
		First(&record).Error
	if err != nil {
		retStatus.Status = "offline"
	} else {
		var orders []RentOrder
		err = db.Model(&RentOrder{}).Debug().
			Where("submit_machine_record_id=?", record.ID).
			Where("rent_start_time <= ? and rent_end_time > ?", t_now, t_now).
			Find(&orders).Error
		if len(orders) <= 0 {
			retStatus.Status = "ready"
		} else {
			retStatus.Status = "renting"

			for _, v := range orders {
				var item WalletRent

				item.RentType = v.RentType
				item.TaskID = v.TaskId

				rent_end := time.Time(v.RentEndTime).Unix()
				item.RentEnd = rent_end

				var rent_wallet string
				var user_account Account
				err = db.Model(&Account{}).Debug().Where("id=?", v.UserId).First(&user_account).Error
				if err == nil {
					rent_wallet = user_account.WalletAddr

					item.Wallet = rent_wallet

					rent_wallets = append(rent_wallets, item)
				}
			}
			retStatus.RentWallet = rent_wallets
		}
	}

	return &retStatus
}
